﻿from rightnow6.locals import *
from rightnow6.base import *
from rightnow6._depd_pygame import \
	PygameGraphSys as GraphSys, \
	PygameSoundSys as SoundSys, \
	PygameAlbum as Album, \
	PygameGraphOperator as GraphOperator, \
	Tick, \
	GetKmState, \
	SaveSurface, \
	GetWaitTime, \
	ClearEvent
	
from pygame.locals import *

imcreate = GraphOperator.create
imcopy = GraphOperator.copy
imconvert = GraphOperator.convert
imissame = GraphOperator.issame
imgrayscale = GraphOperator.grayscale
imchannelenhance = GraphOperator.channelenhance
imchannelpaste = GraphOperator.channelpaste
imsetalphavalue = GraphOperator.setalphavalue
immulalphavalue = GraphOperator.mulalphavalue
imblend = GraphOperator.blend
imlaplacian = GraphOperator.laplacian
iminvert = GraphOperator.invert
imclip = GraphOperator.clip
imflip = GraphOperator.flip
imscale_rate = GraphOperator.scale_rate
imscale_fixed = GraphOperator.scale_fixed
imrotate = GraphOperator.rotate
imtranspose = GraphOperator.transpose
imcolorshape = GraphOperator.colorshape

def onexit(f):
	import atexit
	atexit.register(f)
	return f
	
def ABC_Require(attrtuple):
	def decorate(cls):
		cls.requireattrs = attrtuple
		for k in kwds:
			setattr(f, k, kwds[k])
		return f
	return decorate
	
class GameMeta(type):
	def __instancecheck__(cls, inst):
		#"""Implement isinstance(inst, cls)."""
		#return any(cls.__subclasscheck__(c)
		#		   for c in {type(inst), inst.__class__})
		all_require = all(hasattr(inst, attr) for attr in cls._requireattrs_)
		return all_require and cls._instancecheck_(inst)

	def __subclasscheck__(cls, sub):
		#"""Implement issubclass(sub, cls)."""
		candidates = cls.__dict__.get("_subclass_", set()) | {cls}
		return any(c in candidates for c in sub.mro())
		return all(hasattr(inst, attr) for attr in cls.requireattrs)
	
class Advanceable(metaclass=GameMeta):
	_requireattrs_ = ["advance"]
	_subclass_ = ()
	#def __instancecheck__(cls, instance):
	#	pass 
		
class Moveable(metaclass=GameMeta):
	_requireattrs_ = ["Pos", "MoveTo"]
	_subclass_ = ()
	#def __instancecheck__(cls, instance):
	#	pass 

class Audible(metaclass=GameMeta):
	_requireattrs_ = ["sounds"]
		
class Renderable(metaclass=GameMeta):
	_requireattrs_ = ["surface", "centerpos", "pos"]
	
class Renderedable(metaclass=GameMeta):
	_requireattrs_ = ["dstsurface"]

class Inputable(metaclass=GameMeta):
	_requireattrs_ = ["inputevent"]

	
#每個advanc前進一個frame
#keyframe概念  namingframe概念
class Animation():
	def __init__(self):
		self.frames = [{}]
		self.headpos = 0
		
		
	def Update(self, obj):
		frame = self.frames[self.headpos]
		for key in frame.keys():
			if key in obj.__dict__:
				obj.__dict__[key] = frame[key]
				
	def Attributes(self):
		frame = self.frames[self.headpos]
		for key in frame.keys():
			yield key
		
	def advance(self):
		pass

class DisplayNode(GraphSys):
	def __init__(self, **kwargs):
		GraphSys.__init__(self, **kwargs)
		self.pos = Point(0,0)
		self.childs = []
		
	def Pos(self):
		return self.pos
		
	def open(self):
		GraphSys.open(self)
		self.dstsurface = self._surface()
		
	def advance(self):
		for child in self.childs:
			child.advance()
		
	def inputevent(self, event):
		for child in self.childs:
			child.inputevent(event)
		
		
# 想出一種方法可以讓[屬性設定]或是[方法呼叫]延遲到下個advance開始時
# 用以支援同步

# 想出一種方法可以寫入py檔覆蓋其中的全域變數
class GameNode():
	def __init__(self, pos=Point(0,0), surface = None, centerpos=Point(0,0), name="someone"):
		self.name = name
		self.childs = []
		self.pos = pos
		self.zdepth = 0
		self.dstsurface = None
		
		self.centerpos = centerpos
		self.surface = surface
		
		#如果這樣設定則不顯示圖片
		#self.isvisible = False
		
		#Debug線寬
		#self.border = 1
		#self.bordercolor = RGB(128,128,128)
		#self.showname = False
		return
	
	def MoveTo(self, pos):
		self.pos = pos
		
	def Pos(self):
		return self.pos 
		

	def advance(self):
		return
		
	def inputevent(self, event):
		return
		

	
def InputEvent():
	def __init__(self):
		# points包含mouse + 繪圖板 + webcam
		self.points
		
		# mouse只能有一個
		self.mouse
		
		# 具有彈跳的按鈕
		self.buttons
		
		# 軌跡球(無限延伸的指標) 在第一人稱射擊中滑鼠也算是
		self.trackballs 
		
		# 有界限座標輸入(例如ps的類比搖桿) 該座標與螢幕沒有關係
		self.axis
		
		# 有界限單值輸入
		self.scroll
		
		# 波形輸入(例如音訊)
		self.wave
		
	def translate(self):
		pass
		
	def copy(self):
		return copy.deepcopy(self)
		
class Transform():
	def __init__(self):
		self.blendmode = BLEND_COPY
		
		#self.clip = None		#self.clip = (0,0,0,0)
		#self.scale_rate = None		#self.scale  = (0,0)
		#self.scale_fixed = None		#self.scale  = (0,0)
		#self.rotate = None		#self.rotate  = 0
		#self.flip = None		#self.flip = (False,False)
		#self.alpha = None		#self.alpha  = 0

		
#將TransformRanderRequest轉換成RanderRequeest 隨時可以繪製到另一個surface的狀態
def TransformSurface(node, transform):	
	temp_surface = node.surface
	temp_cp = node.centerpos
	
	if hasattr(transform, "clip") and transform.clip != None:
		width, height = temp_surface.get_size()
		temp_cp = PointAtClipedBBox(temp_cp, width, height, transform.clip)
		temp_surface = imclip(temp_surface, transform.clip)
		
	if hasattr(transform, "scale_rate") and transform.scale_rate != None:
		width, height = temp_surface.get_size()
		scale_ratex, scale_ratey = transform.scale_rate
		temp_cp = PointAtScaleRateBBox(temp_cp, width, height, scale_ratex, scale_ratey)
		temp_surface = imscale_rate(temp_surface, scale_ratex, scale_ratey)
		
	if hasattr(transform, "scale_fixed") and transform.scale_fixed != None:
		width, height = temp_surface.get_size()
		scale_fixedx, scale_fixedy = transform.scale_fixed
		temp_cp = PointAtScaleFixedBBox(temp_cp, width, height, scale_fixedx, scale_fixedy)
		temp_surface = imscale_fixed(temp_surface, scale_fixedx, scale_fixedy)
		
	if hasattr(transform, "rotate") and transform.rotate != None:
		width, height = temp_surface.get_size()
		temp_cp = PointAtRotatedBBox(temp_cp, width, height, transform.rotate)
		temp_surface = imrotate(temp_surface, transform.rotate)
		
	if hasattr(transform, "flip") and transform.flip != None:
		width, height = temp_surface.get_size()
		flipx, flipy = transform.flip
		temp_cp = PointAtFlipedBBox(temp_cp, width, height, flipx, flipy)
		temp_surface = imflip(temp_surface, flipx, flipy)
		
	
	if hasattr(transform, "alpha") and transform.alpha != None:
		alpha = transform.alpha
	else:
		alpha = None
		
	if hasattr(transform, "blendmode")  and transform.blendmode != None:
		blendmode = transform.blendmode 
	else:
		blendmode = BLEND_COPY
	
	return temp_surface, (temp_cp.x, temp_cp.y), blendmode, alpha
	

class RenderNodeCombiner():
	def __init__(self):
		self.nowframe = 0
		self.soundsys = SoundSys()
		#此list裡面儲存著tuple( dst_surface, src_request, point, blend_mode)
		self.blendqueue = []
		
	def clear(self):
		self.blendqueue = []
		
	def extend(self, rootnode):
		stack = [(rootnode, (0,0), rootnode.dstsurface)]
		while(stack):
			lastnode , (father_posx, father_posy), father_dst_surface = stack[-1]
			stack.pop()
			

			if hasattr(lastnode, "invisible") and lastnode.invisible:
				continue
				
			#if renderState.isLocation == True:
			father_posx += lastnode.Pos().x
			father_posy += lastnode.Pos().y
				
			if hasattr(lastnode, "dstsurface") and lastnode.dstsurface:
				father_dst_surface = lastnode.dstsurface
			
			if hasattr(lastnode, "surface") and lastnode.surface:
				if hasattr(lastnode, "transform") and lastnode.transform != None:
					surface, (cpx, cpy), blendmode, alpha = \
					TransformSurface(lastnode, lastnode.transform)
				else:
					surface, (cpx, cpy), blendmode, alpha = \
					(lastnode.surface, (lastnode.centerpos.x, lastnode.centerpos.y), BLEND_COPY, None)
				px = father_posx - cpx
				py = father_posy - cpy
				
				self.blendqueue.append( (father_dst_surface, surface, (px,py), blendmode, alpha) )
				
			if hasattr(lastnode, "sounds") and lastnode.surface:
				for sound in lastnode.sounds:
					self.soundsys.addsound(sound)
					
			if hasattr(lastnode, "childs") and lastnode.childs:
				for child in iter(lastnode.childs):
					stack.append((child, (father_posx, father_posy), father_dst_surface))
			
		
	def blendall(self):
		self.nowframe += 1
		self.soundsys.playback(self.nowframe)
		for i in range(len(self.blendqueue)):
			cpnode = self.blendqueue[i]
			imblend(cpnode[0], cpnode[1], cpnode[2], cpnode[3], cpnode[4])


class Looper():
	def __init__(self, displaynode, fps=60):
		self.nodecombiner = RenderNodeCombiner()
		#self.graph_pygame = PygameGraph(screen_width, screen_height)
		#self.displaynode = DisplayNode()
		self.displaynode = displaynode
		self.displaynode.open()
		
		if(fps < 0):
			raise ValueError("In thread_loop_base::set_interval_ms() interval_ms < 0")
		self._interval_ms = 1000.0 / fps
		self.frameconut = 0
		self.deque_wait_ms = []
		self.fps = 0
		
		self.stop = False
		
		self.km_state = {}
		
	def SetFps(self, fps):
		self._interval_ms = 1000.0 / fps

	def GetFps(self):
		return self.fps
		
	def loop(self):
		self.frameconut = self.frameconut + 1
		Tick(1000.0 / self._interval_ms)
		
		self.deque_wait_ms.append(GetWaitTime())
		if len(self.deque_wait_ms) > 120:
			self.deque_wait_ms.pop(0)
		sum = 1;
		for wm in self.deque_wait_ms:
			sum += wm
		self.fps = 1000.0 / ( 1.0 * sum / len(self.deque_wait_ms))

		
		self.km_state = GetKmState()
		if self.km_state["key-state"][K_F1] == True:
			self.stop = not self.stop
			
		if not self.stop:
			self.displaynode.inputevent(self.km_state)
			self.displaynode.advance()
			
		self.nodecombiner.clear()
		self.nodecombiner.extend(self.displaynode)
		self.nodecombiner.blendall()
		
		stopped = self.displaynode.flip()
		ClearEvent()
		if stopped:
			return False
			
		return True
		
	

global_stopped = False
global_times = 0
def Run(displaynode, times=-1, asynchronous=False, fps=60):
	global global_stopped, global_times
	global_stopped = False
	global_times = times
	#looper = Looper(displaynode, asynchronous, fps)
	looper = Looper(displaynode, fps)
	def loop():
		global global_stopped, global_times
		stopped = False
		while((not stopped) and (not global_stopped) and global_times != 0):
			stopped = not looper.loop()
			if global_times > 0:
				global_times -= 1
	if asynchronous:
		import _thread
		_thread.start_new_thread(loop)
	else:
		loop()
		
def RunEx():
	"""更多設定而且更簡易的Run"""
	pass
	
def Stop():
	global_stopped = True
	
	
"""
def autoreloadmodule(self):
	reloadkeys = []
	for key in self.library.keys():
		path = self.library[key][0]
		if os.stat(path)[stat.ST_MTIME] != self.library[key][2]:
			reloadkeys.append(key)
		
	# reload
	for key in reloadkeys:
		del self.library[key]
		self.fetch(key)
"""

import ctypes
lilithapi = ctypes.CDLL("capi\\lilithapi.dll")

class TestMgr():
	def __init__(self):
		#self.successcount = 0
		#self.failcount = 0
		self.passlist = []
		self.faillist = []
		
	def TestItem(self, func):
		lilithapi.timer_start()
		issuccess = func()
		lilithapi.timer_print()
		name = func.__name__
		if issuccess:
			self.passlist.append(name)
			print("[OK]   %s" %(name))
		else:
			self.faillist.append(name)
			print("[Fail] %s" %(name))
			
def TestGameNode(name, surface1, node):
	dstsurface = imcopy(surface1)
	combiner = RenderNodeCombiner()
	rootnode = GameNode()
	rootnode.dstsurface = dstsurface
	rootnode.childs.append(node)
	combiner.extend(rootnode)
	combiner.blendall()
	SaveSurface(dstsurface, "testimg\\" + name + ".bmp")
	
def createimage3(surface1,surface2):
	node = GameNode(pos=Point(32,32), surface=surface1)
	TestGameNode("simplenode", surface2, node)
	
	node1 = GameNode(pos=Point(32,32))
	node2 = GameNode(pos=Point(50,50), surface=surface1)
	node3 = GameNode(pos=Point(100,100), surface=surface1)
	node1.childs.append(node2)
	node1.childs.append(node3)
	TestGameNode("morenode", surface2, node1)
	
	node = GameNode(pos=Point(0,0), surface=surface1, centerpos=Point(64,64))
	node.transform = Transform()
	node.transform.alpha = 128
	node.transform.scale_rate = (2,2)
	node.transform.rotate = 45
	node.transform.blendmode = BLEND_SUB
	#node.transform.blendmode = pygame.BLEND_RGBA_SUB
	TestGameNode("transnode", surface2, node)



class TestNode():
	def __init__(self, album):
		self.name = "test"
		self.childs = []
		self.pos = Point(0, 0)
		self.centerpos = Point(64, 64)
		self.surface = album.fetch("testerimg\\test128x128_32_1")
	
		self.dir = Vector(1, 1)
	def Pos(self):
		return self.pos
	
	def advance(self):
		self.pos += (self.dir * 5)
		if self.pos.x < 0:
			self.dir = Vector(1, self.dir.y)
		if self.pos.y < 0:
			self.dir = Vector(self.dir.x, 1)
		if self.pos.x > 640:
			self.dir = Vector(-1, self.dir.y)
		if self.pos.y > 480:
			self.dir = Vector(self.dir.x, -1)
			
		
	def inputevent(self, event):
		pass
		
if __name__ == '__main__':
	tester = TestMgr()
	#pygameGrahp = PygameGraph(800, 600)
	#graphsys = GraphSys(hasborder=False, size=Size(100,100))
	
	#graphsys.open()
	album = Album()
	#while(1):
	#	Tick(60)
	#	graphsys.flip()
	displaynode = DisplayNode(hasborder=True, size=Size(640,480))
	#looper = Looper(displaynode, 60)
	#while(1):
	#	looper.loop()
	
	testnode = TestNode(album)
	displaynode.childs.append(testnode)
	
	Run(displaynode)
	
	def TestBarnFetchSurface():
		return str(album.fetch("testerimg\\test128x128_32_1")) == "<Surface(128x128x32 SW)>"
		
	def TestBarnFetchSound():
		return "<Sound object at" in str(album.fetch("testerimg\\close_door_2"))
		
	tester.TestItem(TestBarnFetchSurface)
	tester.TestItem(TestBarnFetchSound)
	#print(album.fetch("test128x128_32_1").get_bytesize())
	
	createimage3(album.fetch("testerimg\\test128x128_32_1"), album.fetch("testerimg\\test256x256_24"))
	